package tracks.controllers.methods.MCTS;

import core.game.Observation;
import core.game.StateObservation;
import ontology.Types;
import tools.Vector2d;

import java.util.*;

public class Heuristic {
    public Heuristic() {
        ;
    }
    public static final double HUGE_POS = 1000000000;
    public static final double HUGE_NEG = -999999999;
    public static final double Game2KeyDistanceCoefficient = 1;
    public static final double Game2PortalDistanceCoefficient = 1;
    public static final double Game0NearestGemstoneCoefficient = -1;
    public static final double Game1RockCountCoefficient = 200;
    public static final double Game1NPCDistanceValueCoefficient = -1000;
    public static final double Game1ATDistanceCoefficient = -0.8;
    public static final double Game1RockDistanceCoefficient = -100;
    public static final double GameScoreCoefficient = 300;


    StateObservation stateObs=null;
    public int blockSz;
    public Vector2d avatarPosition;
    public ArrayList<Observation>[] immovablePositions;
    public ArrayList<Observation>[] portalPositions;


    public double evaluate(StateObservation stateObs) {
        this.stateObs = stateObs;
        blockSz = stateObs.getBlockSize();
        avatarPosition = stateObs.getAvatarPosition();
        immovablePositions = stateObs.getImmovablePositions();
        portalPositions = stateObs.getPortalsPositions();

        // 如果已经获胜或者失败，直接返回。
        if (stateObs.getGameWinner() == Types.WINNER.PLAYER_WINS) return HUGE_POS;
        else if (stateObs.getGameWinner() == Types.WINNER.PLAYER_LOSES) return HUGE_NEG;

        double score = 0;

        // 根据场景中的物品种类设置标志。
        int existsKey_game2 = 0;
        int existsKey_game0 = 0;
        int existsGemstone_game0 = 0;
        int existsTreasure_game1 = 0;
        for (ArrayList<Observation> objs: immovablePositions) {
            if (objs.size() == 0) continue;
            if (objs.get(0).itype == 4) existsKey_game2 = 1;
            if (objs.get(0).itype == 19) existsKey_game0 = 1;
            if (objs.get(0).itype == 5 || objs.get(0).itype == 6) existsGemstone_game0 = 1;
            if (objs.get(0).itype == 10) {
                existsTreasure_game1 = 1;
            }
        }

        // 水迷宫游戏的evaluation
        int isGame2 = 0;
        double keyDistance_game2 = 0;
        double portalDistance_game2 = 0;
        double hasKey_game2=1;
        if (portalPositions != null) {
            Agent.ROLLOUT_DEPTH = 80;
            Vector2d keyPosition_game2 = null;
            isGame2 = 1;

            // 如果存在水迷宫的key，先找key
            if (existsKey_game2 == 1) {
                hasKey_game2 = 0;
                for (ArrayList<Observation> objs : immovablePositions) {
                    if (objs.size() == 0 || objs.get(0).itype != 4) continue;
                    keyPosition_game2 = objs.get(0).position;
                    keyDistance_game2 = -Math.abs(keyPosition_game2.x - avatarPosition.x) - Math.abs(keyPosition_game2.y - avatarPosition.y);
                    break;
                }
            }

            // 找portal
            for (ArrayList<Observation> objs : portalPositions) {
                if (objs.size() == 0 || objs.get(0).itype != 7) continue;
                Vector2d portalPosition = objs.get(0).position;
                if (existsKey_game2 == 0) {
                    portalDistance_game2 = -Math.abs(portalPosition.x - avatarPosition.x) - Math.abs(portalPosition.y - avatarPosition.y);
                }
                else
                    portalDistance_game2 = -Math.abs(portalPosition.x - keyPosition_game2.x) - Math.abs(portalPosition.y - keyPosition_game2.y);
                break;
            }
        }


        // 挖矿游戏的evaluation
        int isGame0 = 0;
        double nearestGemstoneDistance_game0 = Double.MAX_VALUE;
        ArrayList<Observation>[] sortedImmovable = stateObs.getImmovablePositions(avatarPosition);
        for (ArrayList<Observation> objs : sortedImmovable) {
            if (objs.size() == 0) continue;
            int itype = objs.get(0).itype;
            if (itype != 5 && itype != 6 && itype != 8 && itype != 19) continue;
            if (existsTreasure_game1==1 || (itype == 8 && existsKey_game0==1)) continue;
            isGame0 = 1;
            Vector2d nearest = objs.get(0).position;
            double nearestDistance = Math.abs(nearest.x - avatarPosition.x) + Math.abs(nearest.y - avatarPosition.y);
            nearestGemstoneDistance_game0 = (nearestDistance >= nearestGemstoneDistance_game0) ? nearestGemstoneDistance_game0 : nearestDistance;
        }

        // 宝藏守护者的evaluation
        int isGame1 = 0;
        double rockCount_game1=0;
        double npcDistanceValue_game1 = 0;
        double avatarTreasureDistance_game1 = 0;
        double rockDistance_game1 = 0;
        if (existsTreasure_game1 == 1) {
            isGame1 = 1;
            Vector2d treasurePosition = null;
            for (ArrayList<Observation> obs: immovablePositions) {
                if (obs.size()==0 || obs.get(0).itype!=10) continue;
                treasurePosition = obs.get(0).position;
            }
            int treasureX = (int) treasurePosition.x/blockSz;
            int treasureY = (int) treasurePosition.y/blockSz;

            for (ArrayList<Observation> npcs: stateObs.getNPCPositions()) {
                Observation npc = npcs.get(0);
                int npcX = (int)npc.position.x/blockSz;
                int npcY = (int)npc.position.y/blockSz;
                int Xminus = Math.abs(npcX-treasureX)>=4?1:0;
                int Yminus = Math.abs(npcY-treasureY)>=4?1:0;
                npcDistanceValue_game1 += 1.0/(Math.abs(npcX-treasureX)+Math.abs(npcY-treasureY));
                for (int indexX=Math.min(npcX, treasureX)+1; indexX<= Math.max(npcX, treasureX)-1; indexX++) {
                    for (int indexY=Math.min(npcY, treasureY); indexY<=Math.max(npcY, treasureY); indexY++) {
                        for (Observation obs: stateObs.getObservationGrid()[indexX][indexY]) {
                            if (obs.category == 6 || (obs.category==4&&(obs.itype==0||obs.itype==8))) {
                                rockCount_game1 += 1;
                            }
                        }
                    }
                }
            }
            for (Observation rock:stateObs.getMovablePositions()[0]) {
                int rockX = (int)rock.position.x/blockSz;
                int rockY = (int)rock.position.y/blockSz;
                rockDistance_game1 += Math.abs(rockX-treasureX)+Math.abs(rockY-treasureY);
            }
            avatarTreasureDistance_game1 = Math.abs(treasurePosition.x-avatarPosition.x)+Math.abs(treasurePosition.y-avatarPosition.y);
        }


        // 游戏得分
        double gameScore = stateObs.getGameScore();

        // 返回score
        score =   isGame0 * nearestGemstoneDistance_game0 * Game0NearestGemstoneCoefficient
                + isGame1 * (Game1ATDistanceCoefficient*avatarTreasureDistance_game1 + Game1NPCDistanceValueCoefficient*npcDistanceValue_game1+Game1RockCountCoefficient*rockCount_game1+Game1RockDistanceCoefficient*rockDistance_game1)
                + isGame2 * (portalDistance_game2 * Game2PortalDistanceCoefficient + keyDistance_game2 * Game2KeyDistanceCoefficient+1000*hasKey_game2)
                + gameScore * GameScoreCoefficient * (1-isGame1)
                ;


        return score;
    }



}
